package br.pucrio.inf.lac.konoha.hyuga.others.dstream;

import java.net.Inet4Address;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import br.pucrio.inf.lac.konoha.hyuga.common.Grid;
import br.pucrio.inf.lac.konoha.hyuga.common.Pair;

public class GridClusterDStream {
    private Map<Pair<Integer, Integer>, GridCharacteristicVector> coreCells;
    private Map<Pair<Integer, Integer>, GridCharacteristicVector> neighborCells;
    
    public GridClusterDStream() {
        coreCells     = new HashMap<Pair<Integer,Integer>, GridCharacteristicVector>();
        neighborCells = new HashMap<Pair<Integer,Integer>, GridCharacteristicVector>();
    }
    
    public GridClusterDStream(GridCharacteristicVector core, List<GridCharacteristicVector> neighbors) {
        coreCells     = new HashMap<Pair<Integer,Integer>, GridCharacteristicVector>();
        neighborCells = new HashMap<Pair<Integer,Integer>, GridCharacteristicVector>();
        
        Pair<Integer, Integer> index = new Pair<Integer, Integer>(core.getX(), core.getY()); 
        coreCells.put(index, core);
        
        for (GridCharacteristicVector n : neighbors) {
            index = new Pair<Integer, Integer>(n.getX(), n.getY());
            neighborCells.put(index, n);
        }
    }
    
    public void merge(GridClusterDStream otherCluster) {
        coreCells.putAll(otherCluster.getCoreCells());
        neighborCells.putAll(otherCluster.getNeighborCells());

        for (Pair<Integer, Integer> gridIndex : coreCells.keySet()) {
            neighborCells.remove(gridIndex);
        }
    }
    
    public void remove(GridCharacteristicVector cell) {
        Pair<Integer, Integer> cellIndex = new Pair<Integer, Integer>(cell.getX(), cell.getY());
        coreCells.remove(cellIndex);
    }
    
    public void recalculateNeighbors() {
        Set<Pair<Integer, Integer>> newNeighborSet = new HashSet<Pair<Integer,Integer>>();
        
        for (Pair<Integer, Integer> gridIndex : coreCells.keySet()) {
            newNeighborSet.addAll(getNeighbors(gridIndex));
        }
        
        newNeighborSet.removeAll(coreCells.keySet());
        
        Iterator<Pair<Integer, Integer>> it = neighborCells.keySet().iterator();
        while (it.hasNext()) {
            Pair<Integer, Integer> neighborIndex = it.next();
            if (!newNeighborSet.contains(neighborIndex)) {
                it.remove();
            }
        }
    }
    
    private Set<Pair<Integer, Integer>> getNeighbors(Pair<Integer, Integer> cellIndex) {
        Set<Pair<Integer, Integer>> set = new HashSet<Pair<Integer,Integer>>();
        int x = cellIndex.getFirst();
        int y = cellIndex.getSecond();
        
        set.add(new Pair<Integer, Integer>(x - 1, y - 1));
        set.add(new Pair<Integer, Integer>(x - 1, y    ));
        set.add(new Pair<Integer, Integer>(x - 1, y + 1));   
        
        set.add(new Pair<Integer, Integer>(x, y - 1));
        set.add(new Pair<Integer, Integer>(x, y + 1));   
        
        set.add(new Pair<Integer, Integer>(x + 1, y - 1));
        set.add(new Pair<Integer, Integer>(x + 1, y    ));
        set.add(new Pair<Integer, Integer>(x + 1, y + 1));   

        return set;
    }
    
    public List<Set<Pair<Integer, Integer>>> getRemainderClusters() { 
        ArrayList<Set<Pair<Integer, Integer>>> remainderList = new ArrayList<Set<Pair<Integer, Integer>>>();
        Set<Pair<Integer, Integer>> remainderCells = coreCells.keySet();
        
        for (Pair<Integer, Integer> rCell : remainderCells) {
            int cellx = rCell.getFirst();
            int celly = rCell.getSecond();
            boolean neighbor = false;
            
            for (Set<Pair<Integer, Integer>> remainderCluster : remainderList) {
                for (Pair<Integer, Integer> clusterCell : remainderCluster) {
                    if (isNeighbor(cellx, celly, clusterCell.getFirst().intValue(), clusterCell.getSecond().intValue())) {
                        neighbor = true;
                        break;
                    }
                }
                
                if (neighbor) {
                    remainderCluster.add(rCell);
                    break;
                }
            }
            
            if (!neighbor) {
                Set<Pair<Integer, Integer>> s = new HashSet<Pair<Integer,Integer>>();
                s.add(rCell);
                remainderList.add(s);
            }
        }
        
        return remainderList;
    }
    
    public boolean isNeighbor(GridCharacteristicVector cell) {
        Pair<Integer,Integer> cellIndex = new Pair<Integer, Integer>(cell.getX(), cell.getY());
        
        return neighborCells.containsKey(cellIndex);
    }
    
    private boolean isNeighbor(int x1, int y1, int x2, int y2) {
        boolean neighbor = false;
        if (((Math.abs(x1 - x2) <= 1) && (Math.abs(y1 - y2) <= 1)) &&
            ((Math.abs(x1 - x2) != 0) || (Math.abs(y1 - y2) != 0))) {
            neighbor = true;
        }
        return neighbor;
    }


    public boolean contain(Pair<Integer, Integer> index) {
        return coreCells.containsKey(index);
    }

    public Map<Pair<Integer, Integer>, GridCharacteristicVector> getCoreCells() {
        return coreCells;
    }
    
    public Map<Pair<Integer, Integer>, GridCharacteristicVector> getNeighborCells() {
        return neighborCells;
    }
    
    public void setCoreCells(Map<Pair<Integer, Integer>, GridCharacteristicVector> grids) {
        this.coreCells = grids;
    }
    
    public void setNeighborCells(Map<Pair<Integer, Integer>, GridCharacteristicVector> neighborCells) {
        this.neighborCells = neighborCells;
    }
    
    public int size() {
        int size = 0;
        
        for (GridCharacteristicVector coreGrid : coreCells.values()) {
            size += coreGrid.getSize();
        }
        
        for (GridCharacteristicVector neighborGrid : neighborCells.values()) {
            size += neighborGrid.getSize();
        }
        
        return size;
    }
    
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        String delim = "";
        sb.append("CORE CELLS: ");
        for (Pair<Integer, Integer> gridCoreIndex : coreCells.keySet()) {
            sb.append(delim).append("(").append(gridCoreIndex.getFirst()).append(", ").append(gridCoreIndex.getSecond()).append(")");
            delim = ", ";
        }
        sb.append(" Size: ").append(size());
        return sb.toString();
    }

    public String getNodes() {
        StringBuffer sb = new StringBuffer();
        String delim = "";
        sb.append("\"");
        try {
        for (Pair<Integer, Integer> gridCoreIndex : coreCells.keySet()) {
            GridCharacteristicVector cell = coreCells.get(gridCoreIndex);
            for (String luID : cell.getNodes().keySet()) {
                sb.append(delim).append(luID);
                delim = ",";
            }
        } } catch (Exception ex) {
            ex.printStackTrace();
        }
        for (Pair<Integer, Integer> gridNeighborIndex : neighborCells.keySet()) {
            GridCharacteristicVector cell = neighborCells.get(gridNeighborIndex);
            for (String luID : cell.getNodes().keySet()) {
                sb.append(delim).append(luID);
                delim = ",";
            }
        }
        sb.append("\"");
        return sb.toString();
    }
}
